package com.travel.sight.service.impl;

import com.alibaba.fastjson.TypeReference;
import com.travel.common.attraction.AttractionTagMap;
import com.travel.common.es.AttractionEsModel;
import com.travel.common.to.AttractionInfoTo;
import com.travel.common.utils.R;
import com.travel.sight.entity.CommentEntity;
import com.travel.sight.feign.MemberFeignService;
import com.travel.sight.feign.SearchFeignService;
import com.travel.sight.service.CollectionService;
import com.travel.sight.service.CommentService;
import com.travel.sight.service.LikeService;
import com.travel.sight.vo.AttractionVo;
import com.travel.sight.vo.CommentVo;
import com.travel.sight.vo.MemberLevelVo;
import com.travel.sight.vo.MemberVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.travel.common.utils.PageUtils;
import com.travel.common.utils.Query;

import com.travel.sight.dao.AttractionInfoDao;
import com.travel.sight.entity.AttractionInfoEntity;
import com.travel.sight.service.AttractionInfoService;
import org.thymeleaf.util.StringUtils;


@Slf4j
@Service("attractionInfoService")
public class AttractionInfoServiceImpl extends ServiceImpl<AttractionInfoDao, AttractionInfoEntity> implements AttractionInfoService {

    @Autowired
    SearchFeignService searchFeignService;

    @Autowired
    ThreadPoolExecutor executor;

    @Autowired
    CommentService commentService;

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    LikeService likeService;

    @Autowired
    CollectionService collectionService;

    /**
     * 景点信息的模糊搜索
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        //1. 获取key
        String key = (String) params.get("key");
        QueryWrapper<AttractionInfoEntity> queryWrapper = new QueryWrapper<>();
        if(!StringUtils.isEmpty(key)){
            queryWrapper.eq("attraction_id",key)
                    .or().like("name",key).or()
                    .eq("location",key).or()
                    .eq("tag",key);
        }
        IPage<AttractionInfoEntity> page = this.page(
                new Query<AttractionInfoEntity>().getPage(params),
                queryWrapper
        );

        return new PageUtils(page);
    }

    /**
     * 将前端传递的vo对象封装为AttractionInfoEntity
     * @param vo
     * @return
     */
    @Override
    public AttractionInfoEntity voToEntity(AttractionVo vo) {
        AttractionInfoEntity entity = new AttractionInfoEntity();

        //将景点标签的英文转为中文
        String tag = AttractionTagMap.convertToChinese(vo.getTag());
        vo.setTag(tag);

        //属性对拷
        BeanUtils.copyProperties(vo,entity);

        // 设置默认值
        entity.setClickNum(0);
        entity.setBrowseTime(0);
        entity.setCommentNum(0);
        entity.setCollectionNum(0);
        entity.setLikeNum(0);

        entity.setCreateTime(new Date());
        entity.setUpdateTime(new Date());

        // 将图片list转为string，以,分割
        List<String> imageUrl = vo.getImageUrl();
        String join = String.join(",", imageUrl);
        entity.setImageUrl(join);

        return entity;
    }

    /**
     * 首页的景点展示
     * @return
     */
    @Override
    public List<AttractionInfoEntity> showAllAttraction() {
        AttractionInfoDao attractionInfoDao = this.baseMapper;

        List<AttractionInfoEntity> attractions = attractionInfoDao.selectList(null);

        return attractions;


    }

    /**
     * 将景点信息保存到es中
     * @param attractionInfoEntity
     */
    @Override
    public void saveES(AttractionInfoEntity attractionInfoEntity) {
        AttractionEsModel model = new AttractionEsModel();
        //属性对拷
        BeanUtils.copyProperties(attractionInfoEntity,model);

        //TODO 综合分数 = 评分分数×0.6 + 热度分数×0.4 后续使用异步任务定时计算更新排序字段
        model.setSynthesis(0.0);

        //TODO 热度分数
        model.setHotScore(0.0);

        //TODO 评分分数
        model.setScore(0.0);

        R r = searchFeignService.attractionSave(model);
        if(r.getCode() == 0){
            //远程调用成功
            log.info("景点信息保存es成功。。。");
        }else{
            //远程调用失败
            //TODO 重复调用，接口幂等性
        }



    }

    /**
     * 展示景点详情
     * 异步编排 1与2无关  3需要2的结果
     *
     * @param attractionId
     * @return
     */
    @Override
    public AttractionVo info(Integer attractionId) throws ExecutionException, InterruptedException {
        AttractionVo attractionVo = new AttractionVo();

        //1. 展示基本信息
        CompletableFuture<Void> baseInfoFuture = CompletableFuture.runAsync(() -> {
            AttractionInfoEntity attractionInfoEntity = getById(attractionId);
            BeanUtils.copyProperties(attractionInfoEntity, attractionVo);

            //1.1 点赞 收藏 数量从redis中获取最新数据  attraction_info:43:like_num attraction_info:46:like_ num
            String likeKey = "attraction_info:"+attractionId+":like_num";
            String collectionKey = "attraction_info:"+attractionId+":collection_num";
            String s1 = redisTemplate.opsForValue().get(likeKey);
            String s2 = redisTemplate.opsForValue().get(collectionKey);

            //1.2 为null 说明还没有点赞或收藏的记录
            if(s1 != null){
                attractionVo.setLikeNum(Integer.valueOf(s1));
            }else{
                attractionVo.setLikeNum(0);
            }
            if(s2 != null){
                attractionVo.setCollectionNum(Integer.valueOf(s2));
            }else{
                attractionVo.setCollectionNum(0);
            }

        }, executor);


        //2. 展示评论信息
        CompletableFuture<List<CommentEntity>> commentListFuture = CompletableFuture.supplyAsync(() -> {
            return commentService.getCommentList(attractionId);
        }, executor);

        //3. 查询并赋值 评论列表中用户信息
        CompletableFuture<Void> userInfoFuture = commentListFuture.thenAcceptAsync((commentList) -> {
            List<CommentVo> commentVoList = new ArrayList<>();
            for (CommentEntity entity : commentList) {
                Integer userId = entity.getUserId();
                //3.1 远程调用获取用户信息
                R r = memberFeignService.info(userId);
                if (r.getCode() == 0) {
                    MemberVo memberVo = r.getData("member",new TypeReference<MemberVo>() {
                    });
                    CommentVo commentVo = new CommentVo();
                    // 属性对拷
                    BeanUtils.copyProperties(entity, commentVo);
                    commentVo.setUsername(memberVo.getUsername());
                    //3.2 远程调用获取会员等级信息
                    R r1 = memberFeignService.memberLevelInfo(memberVo.getLevelId());
                    if (r1.getCode() == 0) {
                        MemberLevelVo memberLevelVo = r1.getData("memberLevel",new TypeReference<MemberLevelVo>() {
                        });
                        commentVo.setLevelName(memberLevelVo.getName());
                    }
                    commentVo.setHeader(memberVo.getHeader());

                    commentVoList.add(commentVo);
                }
            }
            attractionVo.setCommentList(commentVoList);
        }, executor);

        //等待所有异步任务完成
        CompletableFuture.allOf(baseInfoFuture,commentListFuture,userInfoFuture).get();

        return attractionVo;

    }

    /**
     * 获取用户的点赞、收藏、评论总次数
     * @param userId
     * @return
     */
    @Override
    public Integer getBehaviorNum(Integer userId) {
        // 点赞次数
        Integer likeNum = likeService.getLikeNumByUserId(userId);
        // 收藏次数
        Integer collectionNum = collectionService.getCollectionNumByUserId(userId);
        // 评论次数
        Integer commentNum = commentService.getCommentNumByUserId(userId);

        return likeNum+collectionNum+commentNum;
    }

    /**
     * 获取该标签的所有景点
     * @param tagName
     * @return
     */
    @Override
    public List<AttractionInfoTo> getAttractionByTagName(String tagName) {
        List<AttractionInfoEntity> attractionInfoEntityList = this.baseMapper.selectList(new QueryWrapper<AttractionInfoEntity>().eq("tag", tagName));
        // 把AttractionInfoEntity都转为AttractionInfoTo 传输到个性化推荐服务
        List<AttractionInfoTo> attractionInfoTos = attractionInfoEntityList.stream().map(entity -> {
            AttractionInfoTo attractionInfoTo = new AttractionInfoTo();
            BeanUtils.copyProperties(entity, attractionInfoTo);
            return attractionInfoTo;
        }).collect(Collectors.toList());

        return attractionInfoTos;
    }

    /**
     * 根据attractionid的集合获取AttractionInfoTo集合
     * @param ids
     * @return
     */
    @Override
    public List<AttractionInfoTo> getAttractionsByIds(List<Integer> ids) {
        List<AttractionInfoEntity> attractionInfoEntityList = this.baseMapper.selectBatchIds(ids);

        List<AttractionInfoTo> attractionInfoTos = attractionInfoEntityList.stream().map(entity -> {
            AttractionInfoTo attractionInfoTo = new AttractionInfoTo();
            BeanUtils.copyProperties(entity, attractionInfoTo);
            return attractionInfoTo;
        }).collect(Collectors.toList());

        return attractionInfoTos;
    }

    /**
     * 获取所有的景点信息
     * @return
     */
    @Override
    public List<AttractionInfoTo> getAllAttraction() {
        List<AttractionInfoEntity> attractionInfoEntityList = this.baseMapper.selectList(null);

        List<AttractionInfoTo> attractionInfoTos = attractionInfoEntityList.stream().map(entity -> {
            AttractionInfoTo attractionInfoTo = new AttractionInfoTo();
            BeanUtils.copyProperties(entity, attractionInfoTo);
            return attractionInfoTo;
        }).collect(Collectors.toList());

        return attractionInfoTos;

    }

}